package com.tt.fashionWebSite.util;

import java.util.ArrayList;
import java.util.List;

/**
 * Created by isoft-work 5 on 11/22/14.
 */
public class Rule {
   /**
    * Trong result bao gồm: I, SP, C, F , consist
    * I là list chứa giá trị có xuất hiện trong hoá đơn hay không của sản phẩm i1, i2, i3...... value {0,1,0,1}; {1,1,0,0}
    * F là list tập ứng cử viên bao gồm {{i1}, {i2},...} ; {{i1,i2}, {i1,i3}...} value chỉ số {{1}, {2},...} ; {{1,2}, {1,3},....}
    * SP là list tập phổ biến của tập ứng cử viên value là kết quả tính được {0.4, 0.6, 0.7}...
    * C là list tập phổ biến 1 phần tử, 2 phần tử {{i1}, {i2},...} ; {{i1,i2}, {i1,i3}...} value chỉ số {{1}, {2},...} ; {{1,2}, {1,3},....}
    * chỉ số sp = chỉ số f
    * consist là tập mặt hàng được rút ra từ C thoả điều kiện để tạo tập ứng cử viên F tiếp theo
    * left là vế trái của luật tìm ra
    * right là vế phải của luật tìm ra
    * CF là độ tin cậy của luật
    */

   private List<Integer[]> I = new ArrayList<Integer[]>();
   private List<List<Integer[]>> F = new ArrayList<List<Integer[]>>();
   private List<List<Integer[]>> C = new ArrayList<List<Integer[]>>();
   private List<Double[]> SP = new ArrayList<Double[]>();
   private Integer[] consist;

   private Double minSupp;
   private Double minConf;

   private List<List<Integer>> left = new ArrayList<List<Integer>>();
   private List<List<Integer>> right = new ArrayList<List<Integer>>();
   private List<Double> CF = new ArrayList<Double>();

   /**
    *
    * @param maTrix ma trận của sản phẩm và hoa đơn
    * @param column số cột của ma trận = số sản phẩm
    * @param row số dòng của ma trận = số hoá đơn
    * @param minSupp ngưỡng minSupp để tìm tập phổ biến tối đại
    * @param minConf ngưỡng minSupp để tìm tập luật từ tập phổ biến tối đại đã rút gọn
    * sẽ khỏi tạo nên I , I = F một phần tử, tính SP của F một phần tử
    */
   public Rule(Integer[][] maTrix, Integer column, Integer row, Double minSupp, Double minConf) {
      // khởi tạo minSupp, minConf
      this.minSupp = minSupp;
      this.minConf = minConf;

      //khởi tạo I, F một phần tử, SP của F một phần tử , tìm được C một phần tử

      List<Integer[]> Fi = new ArrayList<Integer[]>();  // f1, f2, f3,....
      List<Integer[]> Ci = new ArrayList<Integer[]>();
      Double[] SPi = new Double[column]; // sp1, sp2, sp3,......

      for(Integer n = 0; n < column; n++) {
         Integer[] f = new Integer[1];
         Integer[] Ii = new Integer[row];  // i1, i2, i3,....
         Integer count = 0;
         for(Integer j = 0; j < row; j++) {
            Ii[j] = maTrix[j][n];
            if(Ii[j] == 1) {
               count += 1;
            }
         }
         //thêm Ii vào I
         this.I.add(Ii);
         //thêm f vào Fi
         f[0] = n;
         Fi.add(f);
         // kiểm tra và lấy c vào Ci
         SPi[n] = Double.parseDouble(String.valueOf(count))/Double.parseDouble(String.valueOf(row));
         if(SPi[n] >= this.minSupp) {
            Ci.add(Fi.get(n));
         }
      }
      this.F.add(Fi);
      this.SP.add(SPi);
      this.C.add(Ci);
   }

   /**
    *
    * @param Ci là list tập phổ biến mới nhất
    * @return
    */
   // lấy mặt hàng phù hợp mới để tạo ra F tiếp theo
   private Integer[] getConsist(List<Integer[]> Ci) {
      List<Integer> temConsist = new ArrayList<Integer>();
      if(Ci != null && Ci.size() > 0) {
         // lấy c0
         for(Integer k = 0; k < Ci.get(0).length; k++) {
            temConsist.add(Ci.get(0)[k]);
         }
         // kiểm tra phần tử trong c1,c2... nếu không có trong temConsist thì thêm vào
         for(Integer i = 1; i < Ci.size(); i++) {
            Integer[] element = Ci.get(i);
            for(Integer j = 0; j < element.length; j++) {
               for (Integer m = 0; m < temConsist.size(); m++){
                  if(temConsist.get(m) == element[j]) {
                     break;
                  }
                  else {
                     if(m == temConsist.size() - 1) {
                        temConsist.add(element[j]);
                     }
                  }
               }
            }
         }
      }
      // convert list sang mảng
      Integer[] list = new Integer[temConsist.size()];
      return temConsist.toArray(list);
   }

//   lấy tập ứng cứ viên F tiếp theo
   private List<Integer[]> getFi(List<Integer[]> Ci, Integer[] consistI) {
      List<Integer[]> Fi = new ArrayList<Integer[]>();
      for(Integer i = 0; i < Ci.size(); i++) {
         Integer[] c = Ci.get(i);
         for(Integer j = 0; j < consistI.length; j++) {
            for(Integer k = 0; k < c.length; k++) {
               if(consistI[j] == c[k]) {
                  break;
               }
               else {
                  if(k == c.length - 1) {
                     // TODO dùng List để thêm được phân tử mới
                     List<Integer> ctem = new ArrayList<Integer>();
                     for(Integer t = 0; t < c.length; t++) {
                        ctem.add(c[t]);
                     }
                     ctem.add(consistI[j]);
                     Integer[] ctem2 = new Integer[ctem.size()];
                     Fi.add(ctem.toArray(ctem2));
                  }
               }
            }
         }
      }
      // rút gọn Fi xoá những f bị trùng
      for (Integer i = 0; i < Fi.size() - 1; i++) {
         for(Integer j = i + 1; j < Fi.size(); j++) {
            Integer count = 0;
            for(Integer m = 0; m < Fi.get(i).length; m++) {
               for(Integer n = 0; n < Fi.get(j).length; n++) {
                  if(Fi.get(i)[m] == Fi.get(j)[n]) {
                     count += 1;
                  }
               }
            }
            if(count == Fi.get(j).length) {
               if(Fi.indexOf(Fi.get(j)) >= 0 ) {
                  Fi.remove(Fi.indexOf(Fi.get(j)));
                  count = 0;
                  j -= 1;
               }
            }
         }
      }
      return Fi;
   }

//   tính sp
   private Double[] getSPi(List<Integer[]> Fi, List<Integer[]> i) {
      Double[] SPi = null;
      Integer[] tem = null;
      Integer count = 0;
      if(Fi != null) {
         SPi = new Double[Fi.size()];
        tem = new Integer[i.get(0).length];
         // Cộng dãy bit của sản phẩm
         for(Integer n = 0; n < Fi.size(); n++) {
            tem = i.get(Fi.get(n)[0]);
            for(Integer j = 1; j < Fi.get(n).length; j++) {
               Integer[] i2 = i.get(Fi.get(n)[j]);
               for (Integer m = 0; m < tem.length; m++) {
                  if(tem[m] == 1 && i2[m] == 1){
                     tem[m] = 1;
                     if(j == Fi.get(n).length - 1) {
                        count += 1;
                     }
                  }
               }
            }
            SPi[n] = Double.parseDouble(String.valueOf(count))/Double.parseDouble(String.valueOf(i.get(0).length));
            count = 0;
         }
      }
      return SPi;
   }

//   lấy ra tập C
   private List<Integer[]> getCi(List<Integer[]> Fi, Double[] SPi) {
      List<Integer[]> Ci = new ArrayList<Integer[]>();
      if(Fi != null && SPi != null) {
         for(Integer i = 0; i < SPi.length; i++) {
            if(SPi[i] >= this.minSupp) {
               Ci.add(Fi.get(i));
            }
         }
      }
      return Ci;
   }

   // lấy tập C tối đại sau khi tìm hết tất cả tập hợp C
   private List<Integer[]> getFinalC(List<List<Integer[]>> finalC) {
      List<Integer[]> result = new ArrayList<Integer[]>();
      if(finalC != null && finalC.size() > 0) {
         if(finalC.get(finalC.size()-1) != null && finalC.get(finalC.size()-1).size() > 0) {
            result.add(finalC.get(finalC.size()-1).get(0));
            for(int n = finalC.size() - 1; n >= 0; n--) {
               List<Integer[]> Ci = finalC.get(n);
               for(int i = 0; i < Ci.size(); i++) {
                  int count = 0;
                  Integer[] ci = Ci.get(i);
                  for(int j = 0; j < result.size(); j++) {
                     Integer[] ri = result.get(j);
                     // ci có là con của ri
                     if(!check(ci, ri)) {
                        count += 1;
                     }
                  }

                  if(count == result.size()) {
                     result.add(ci);
                  }
               }
            }
         }
      }
      return result;
   }

   // kiểm tra hai mảng ci có là con của ri
   private Boolean check(Integer[] ci, Integer[] ri) {
      Boolean result = false;
      int count = 0;
      for(int i = 0; i < ri.length; i++) {
         for (int j = 0; j < ci.length; j++) {
            if(ri[i] == ci[j]) {
               count += 1;
               break;
            }
         }
         if(count == ci.length) {
            result = true;
            break;
         }
      }
      return result;
   }

//   chạy giải thuật để tìm ra tập phổ biến tối đại
   public void runRule() {
      // một phần tử đã được khởi tạo chạy tiếp 2 phần tử
      Integer index = 0, eNumber = 1;
      while (true) {
         if(this.C.size() > 0) {
            this.consist = getConsist(this.C.get(index));
            if(this.consist.length > eNumber) {
               List<Integer[]> Fi = getFi(this.C.get(index), this.consist);
               Double[] SPi = getSPi(Fi, this.I);
               List<Integer[]> Ci = getCi(Fi, SPi);

               if(Ci.size() > 0) {
                  this.F.add(Fi);
                  this.SP.add(SPi);
                  this.C.add(Ci);
                  index += 1;
                  eNumber += 1;
               }
               else {
                  break;
               }
            }
            else {
               break;
            }
         }
         else {
            break;
         }
      }

      // TODO get CFinal và getRule
      List<Integer[]> finalC = getFinalC(this.C);
      getRule(finalC);
   }

//   Rút ra luật khi tìm được tập phổ biến tối đại
   private void getRule(List<Integer[]> finalC) {
     for(int i = 0; i < finalC.size(); i++) {
        Integer[] c = finalC.get(i);
        // lấy SP của c
        Double spC = this.SP.get(c.length - 1)[this.F.get(c.length - 1).indexOf(c)];
        // lấy vế trái vế phải từ c tạm thời tính độ tin cậy nếu >= minConf thì add vào
        if(c.length > 1) {
           List<Integer[]> allLeft = new ArrayList<Integer[]>(getAllLeft(c));
           for(int j = 0; j < allLeft.size() - 1; j++) {
              // lấy hết vế trái
              Integer[] left = allLeft.get(j);
              List<Integer> li = new ArrayList<Integer>();
              // chuyển mảng sang list
              for(int k = 0; k < left.length; k++) {
                 li.add(left[k]);
              }
              // tính độ tin cậy
              // TODO tìm index của vế trái trong Fi (indexOf không lấy được)
              Integer a = getIndex(this.F.get(li.size() - 1), li);

              Double spli = this.SP.get(li.size() - 1)[a];
              if(spli < spC) {
                 continue;
              }
              else {
                 Double CFi = spC / spli;
                 if (CFi >= this.minConf) {
                    this.right.add(right(c, left));
                    this.left.add(li);
                    this.CF.add(CFi);
                 }
              }
           }
        }
     }
   }

   private Integer getIndex(List<Integer[]> Fi,List<Integer> li) {
      int r = 0;
      for(int i = 0; i < Fi.size(); i++) {
         Integer[] f = Fi.get(i);
         int count = 0;
         for(int j = 0; j < f.length; j++) {
            for (int n = 0; n < li.size(); n++) {
               if (f[j] == li.get(n)) {
                  count += 1;
               }
            }
         }

         if(count == li.size()) {
            r = i;
            break;
         }
      }
      return r;
   }

   private List<Integer[]> getAllLeft(Integer[] c) {
      List<Integer[]> result = new ArrayList<Integer[]>();
      List<Integer[]> resultTem = new ArrayList<Integer[]>();
      // lấy 1 phần tử
      for (Integer i = 0; i < c.length; i++) {
         Integer[] tem = new Integer[1];
         tem[0] = c[i];
         result.add(tem);
      }
      // lấy từ 2 phần tử trên đi
      resultTem = result;
      for (Integer i = 2; i < c.length; i++) {
         resultTem = getFi(resultTem, c);
         for(Integer j = 0; j < resultTem.size(); j++) {
            result.add(resultTem.get(j));
         }
      }
      return result;
   }

   private List<Integer> right(Integer[] c, Integer[] li) {
      List<Integer> ri = new ArrayList<Integer>();
      for(Integer i = 0; i < c.length; i++) {
         Integer count = 0;
         for (Integer j = 0; j < li.length; j++) {
            if(li[j] == c[i]) {
               break;
            }
            else {
               count += 1;
            }
            if(count == li.length) {
               ri.add(c[i]);
               count = 0;
            }
         }
      }
      return ri;
   }

   public List<List<Integer>> getLeft() {
      return left;
   }

   public void setLeft(List<List<Integer>> left) {
      this.left = left;
   }

   public List<List<Integer>> getRight() {
      return right;
   }

   public void setRight(List<List<Integer>> right) {
      this.right = right;
   }

   public List<Double> getCF() {
      return CF;
   }

   public void setCF(List<Double> CF) {
      this.CF = CF;
   }
}
